Clean Architecture 達人に学ぶソフトウェアの構造と設計 メモ
Clean Architecture 達人に学ぶソフトウェアの構造と設計を読んだメモ
1章 設計とアーキテクチャ
ソフトウェアアーキテクチャの目的は、求められるシステムを構築/保守するために必要な人材を最小限におさえること
速く進む唯一の方法は、うまく進むことである
短期的にも長期的にも、崩壊したコードを書くほうがクリーンなコードを書くよりも常に遅い
TDDを使ったほうが作業時間が短かった
2章 2つの価値のお話
全てのソフトウェアシステムは、ステークホルダーに振る舞いと構造という2つの価値を提供する
変更の難易度は、変更の形状ではなく変更のスコープに比例しなければいけない
新しい機能の形状にとらわれないアーキテクチャにしたほうが実用的である
完璧に動作するよりも変更が容易な方が良い
ソフトウェア開発チームには、機能の緊急性よりもアーキテクチャの重要性を強く主張する責任が求められる
アーキテクチャを後回しにするとシステムの開発コストはますます高くなり、システムの一部または全部が変更不能になる
3章 パラダイムの概要
構造化プログラミング
直接的な制御の移行に規律を課す
goto文をif/then/else,do/while/untilに置き換えた
オブジェクト指向プログラミング
間接的な制御の移行に規律を課す
関数型プログラミング
代入に規律を課す
これらのパラダイムはプログラマが何をすべきでないかを伝える
コンポーネントの分離, データ管理, 機能の3つの関心事に対応している
4章 構造化プログラミング
あらゆるプログラムは順次, 選択, 反服の3つの構造で構築できる
現代の言語では無制限のgoto文がサポートされていない
テストによってプログラムが正しくないことは証明できるが、正しいことは証明できない
ソフトウェアは科学のように、反証可能性によって動かされている
アーキテクトは、テスト可能な(=簡単に反証できる)モジュール、コンポーネント、サービスを定義しようとする
5章 オブジェクト指向プログラミング
カプセル化
継承
ポリモーフィズム
依存関係逆転
インターフェースを関数呼び出し間に挿入することで、依存関係が逆転する
独立デプロイ可能性, 独立開発可能性
オブジェクト指向とは、ポリモーフィズムを使用することで、システムにある全てのソースコードの依存関係を絶対的に制御する能力
6章 関数型プログラミング
競合状態, デッドロック, 並行更新の問題の起因は、可変変数にある
ストレージとプロセッサ速度が無限にあるわけではない
最も一般的な妥協は、可変コンポーネントと不変コンポーネントに分け、トランザクションメモリを用いて並行更新や競合状態から保護する
完全に不変になれば、Event Sourcingになる
状態ではなくトランザクションを保存する。状態が必要になるたびに計算する
SOLID原則について
7章 SRP 単一責任の原則
モジュールはたった1つのアクターに対して責務を負うべきである
アクターの異なるコードは分割すべき
Facade パターン
8章 OCPオープン・クローズドの原則
ソフトウェアの振る舞いは、既存の成果物を変更せずに拡張できるようにすべき
上位レベルのコンポーネントが下位レベルのコンポーネントの変更の影響を受けないようにする
9章 LSP リスコフの置換原則
置換可能な性質
リスコフの置換原則はインターフェースと実装に関するソフトウェア設計の原則
10章 ISP インターフェース分離の原則
必要ないものに依存しているソフトウェアに依存することは予期せぬトラブルに繋がる
11章 DIP 依存関係逆転の原則
ソースコードの依存関係が具象ではなく抽象だけを参照しているものが最も柔軟なシステムである
ここでいう具象とは、システム内で変化しやすい具象要素
コーディングレベルのプラクティスにまとめると
変化しやすい具象クラスを参照しない
変化しやすい具象クラスを継承しない
具象関数をオーバーライドしない
変化しやすい具象を名指しで参照しない
12章 コンポーネント
コンポーネント = デプロイの単位
良くできたコンポーネントは常に個別にデプロイできる状態を保っているため個別に開発を進められる
昔はプログラムがリロケータブルではなかった
メモリが高価だったため、コンパイラが低速なデバイスから読み込んだソースコードを繰り返し走査していた
ライブラリ関数のコードをアプリケーションから分離し、個別にコンパイルして既知の番地に配置するようにした
コンパイラが出力するバイナリコードに手を加え、スマートローダでメモリに再配置できるようにした
プログラムからライブラリ関数を呼ぶとき、コンパイラが外部参照を出力するように
ローダが外部参照をリンクできるように
プログラムが巨大化すると、リンクローダの処理が重くなってきた
リンクとロードを別のアプリケーションに切り出し、リンカと呼ばれるものができた
動的にリンクされたファイルを実行時にプラグインできるようになった
13章 コンポーネントの凝集性
REP 再利用/リリース等価の原則
再利用の単位とリリースの単位は等価になる
CCP 閉鎖性共通の原則
同じ理由、同じタイミングで変更されるクラスをコンポーネントにまとめる
変更の理由やタイミングが異なるクラスは別のコンポーネントに分ける
CRP 全再利用の原則
コンポーネントのユーザーに対して、実際には使わないものへの依存を強要してはいけない
密結合していないクラスを同じコンポーネントにまとめるべきではない
コンポーネントの凝集性は複雑である。開発時の利便性と再利用性はトレードオフ
14章 コンポーネントの結合
ADP 非循環依存関係の原則
コンポーネントの依存グラフに循環依存があってはいけない
有向非循環グラフになる
循環依存を排除するためにDIPを利用する
SDP 安定依存の原則
安定度の高い方向に依存すること
SAP 安定度/抽象度等価の原則
コンポーネントの抽象度はその安定度と同程度でなければいけない
15章 アーキテクチャとは?
優れたアークテクトは
方針と詳細を慎重に区別して、方針が詳細を把握すること無く、決して依存することがないようにに両者を切り離す
詳細の決定をできるだけ延期、留保できるように方針をデザインする
16章 独立性
優れたアーキテクチャは、以下のことをサポートしなければ行けない
システムのユースケース
システムの運用
システムの開発
システムのデプロイ
上記のバランスを取るのはユースケースの把握、運用上の制約、チーム構造がわからない、変化するので難しい
バランスを上手く取るため、できるだけ長い期間できるだけ多く選択肢を残すことが可能になる
ユースケースとビジネスルールは異なる理由で変更されるため、レイヤーを切り離すべき
レイヤーを切り離すことで、独立した開発、独立したデプロイが可能である
切り離し方式
ソースレベル
ソースコードモジュール間の依存性を管理する
コンポーネントはすべて同じアドレス空間で実行される
モノリシック構造
デプロイレベル
デプロイ可能な単位の依存性を管理する
コンポーネントは単独でデプロイ可能
サービスレベル
ネットワークパケットだけで通信する
ソースやバイナリを変更してもお互いに影響を与えない
プロジェクトの初期では切り離しを判断するのは難しい
17章 バウンダリー : 境界線を引く
ソフトウェアアーキテクトとは、ソフトウェアの要素を分離し、お互いのことがわからないように制限するというもの
システムの構築/維持するために必要な人材を最小限に抑えるとき、システムのビジネス要件と関係のない決定(フレームワーク、DB、ウェブサーバーなど)が早すぎることが邪魔をする
18章 境界の解剖学
システムの境界は、ローカルの境界とレイテンシーに影響される境界が混在している
19章 方針とレベル
ソフトウェアシステムは方針を示すもの
レベルは入力と出力からの距離を表す
20章 ビジネスルール
エンティティ
最重要ビジネスデータを操作する最重要ビジネスルールをいくつか含んだもの
ユースケース
アプリケーション固有のビジネスルールを記述したもの
入力と出力を持ち、特定のインターフェースに依存しない
21章 叫ぶアーキテクチャ
アーキテクチャはシステムで使用するフレームワークではなく、システムそのものの情報を伝える
22章 クリーンアーキテクチャ
過去のアーキテクチャはどれも関心事の分離という共通の目的を持つ
特徴
フレームワーク非依存
テスト可能
UI非依存
データベース非依存
外部エージェント非依存
https://gyazo.com/9aa5c5ed4940baea3013baa6ef0bcdf2
ソースコードの依存性は内側(上位レベルの方針)だけに向かっていなければいけない
エンティティ
最重要ビジネスルールをカプセル化したもの
ユースケース
アプリケーション固有のビジネスルール
インターフェースアダプタ
外部エージェントのフォーマットへとデータを変換する
フレームワーク、ドライバ
逆の依存の方向に呼び出すにはDIPを使う
23章 プレゼンターとHumble Object
Humble Object パターン
テストしにくい振る舞いをView、テストしやすい振る舞いをPresenterとする
24章 部分的な境界
Strategyパターン
DIPで ServiceImpl を使用する
Facadeパターン
境界をFacadeクラスで定義しておき、全てのサービスのメソッドを呼び出す
25章 レイヤーと境界
コストを評価し、アーキテクチャの境界がどこにあるのか、どこまで実装するべきか判断する必要がある
26章 メインコンポーネント
mainは最下位のコンポーネント
初期状態や構成を設定し、入れ替える
27章 サービス あらゆる存在
サービスはシステムのスケーラビリティや開発の利便性に対しては有用だが、アーキテクチャにおいては重要な要素ではない
28章 テスト境界
ソフトウェア設計において、変化しやすいものに依存しないのは重要
システムの一部として設計されていないテストは脆弱で保守が難しい
29章 クリーン組込みアーキテクチャ
組込みシステムにおいて、ソフトウェア、OS、ファームウェア、ハードウェアの複数のレイヤーに責務が分かれる
OSAL, HALなどが各レイヤーの役割を抽象化している
30章 データベースは詳細
データモデルはアーキテクチャ的には重要だが、その技術やシステムはアーキテクチャ的には重要ではない
31章 ウェブは詳細
GUIは詳細である
32章 フレームワークは詳細
フレームワークを使うことは問題ないが、結合しないことが大切
アーキテクチャの境界から可能な限り遠ざけるように
33章 事例: 動画販売サイト
SRPに基づく分割、依存性のルールによる分割を行った
変更の理由に対応するのはアクターによる分割、変更の頻度に対応するのは方針のレベルの違い
34章 書き残したこと
レイヤーによるパッケージング
機能によるパッケージング
ドメインの概念集約ルートに基づいて分割
ポートとアダプター
コンポーネントによるパッケージング
チームの規模やメンバーのスキル、ソリューションの複雑さ、時間と予算の成約などをなどを考慮する